#pragma once

/*--------------------------------------------------------------------------*/

struct RMacro
{
private:
	std::string					m_name;
	std::string					m_value;

public:
	RMacro(const std::string& name, const std::string& value);

	const std::string&			GetName() const;
	const std::string&			GetValue() const;
};

/*--------------------------------------------------------------------------*/

struct RConstantPtn
{
	friend class RShader;
	friend class RMaterial;
private:
	EShader::Type				m_stage;
	int							m_offset;
	int							m_size;
	const RConst*				m_constant;

public:
	RConstantPtn(const RConst* constant, int size, int offset, EShader::Type stage);

	EShader::Type				GetStage() const;
	int							GetSize() const;
	int							GetOffset() const;
	const RConst*				GetRConst() const;
};

/*--------------------------------------------------------------------------*/

class RSampler 
{
	friend class MaterialRequest;
	friend class RMaterial;
	friend class MaterialMgr;
private:
	std::string					m_name;
	const RSamplerState*		m_sampler;
	const RTexture*				m_texture;
	int							m_location;

public:
	RSampler(const std::string& name, const RSamplerState* m_sampler, const RTexture* m_texture, int location);

	const std::string&			GetSamplerName() const;
	const RSamplerState*		GetSamplerState() const;
	const RTexture*				GetTexture() const;
	int							GetLocation() const;
};

/*--------------------------------------------------------------------------*/

class RConstantValue
{
	friend class MaterialMgr;
private:
	EEngineUniform::Type	m_type;
	uchar					m_value[16];
	uint					m_size;

public:
	RConstantValue(EEngineUniform::Type type, void* data)
	{
		m_type = type;
		m_size = g_Renderer->GetEngineUniformSize(type);
		memcpy(m_value, data, m_size);
	}

	uint GetSize() const
	{
		return m_size;
	}

	void* GetData() const
	{
		return (void*) m_value;
	}
};

/*--------------------------------------------------------------------------*/

namespace ETechnique
{
	enum Type
	{
		Normal			= 1 << 0,
		Instanced		= 1 << 1
	};
}

class RMaterial
{
	friend class MaterialRequest;
	friend class CRenderer;
	friend class MaterialMgr;
	friend class PostprocessMgr;
public:
	RMaterial(const std::string& name);
	~RMaterial();

	const std::string&				GetMaterialName() const;
	const RRasterizerState*			GetRasterizerState() const;
	const RDepthstencilState*		GetDepthstencilState() const;
	const RBlendState*				GetBlendState() const;
	const RSamplerState*			GetSamplerState(uint i) const;
	const RTexture*					GetTexture(uint i) const;
	
	uint							GetProgramHandle() const;
	uint							GetNumSamplers() const;
	uint							GetLocation(uint i) const;
	
	void							UpdateConstants(uint vOffset, uint gOffset, uint fOffset, RBuffer* buffer) const;
	void							Override(RMaterial* copy);

	const RMaterial*				GetNormalVersion() const;
	const RMaterial*				GetInstancedVersion() const;

private:
	bool							CreateProgram(const std::string& vCode, const std::string& gCode, const std::string& fCode);
	void							PreprocessCode(std::string& out);
	uint							CompileShader(const std::string& code, GLenum type);
	uint							AttachShader(const std::string& macro, GLenum gltype, const std::string& defines, const std::string& code);
	int								AttachUBO(const std::string& name, uint binding, EShader::Type stage);

	uint							m_handle;

	const RMaterial*				m_normalVersion;
	const RMaterial*				m_instancedVersion;
	
	std::string						m_materialName;

	const RRasterizerState*			m_rasterizerstate;
	const RDepthstencilState*		m_depthstencilstate;
	const RBlendState*				m_blendstate;

	std::vector<RMacro>				m_macros;
	std::vector<RConstantPtn>		m_consts;
	std::vector<RSampler>			m_samplers;
	std::vector<RConstantValue>		m_constsValue;

	uint							m_sizeofVconsts;
	uint							m_sizeofGconsts;
	uint							m_sizeofFconsts;

	std::string						m_vCode;
	std::string						m_fCode;
	std::string						m_gCode;
};

